#include <pspkernel.h>
#include <pspdisplay.h>
#include <pspdebug.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

#include <pspgu.h>
#include <pspgum.h>
#include <pspctrl.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "../../core/base/timer.h"
#include "../../app_frame/bitmap.h"
#include "../../app_frame/app_frame.h"

// Define the module info section 
PSP_MODULE_INFO("KNyetApp", 0, 1, 1);
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);
PSP_HEAP_SIZE_KB(20480);

// Define printf, just to make typing easier 
#define printf	pspDebugScreenPrintf

// PSP specific defines
#define BUF_WIDTH (512)
#define SCR_WIDTH (480)
#define SCR_HEIGHT (272)

void dump_threadstatus(void);

// Main entry points of the App
bool InitApp();
void DestoryApp();
bool TimeElapse(double sec);

volatile int done = 0;

/* Exit callback */
int exit_callback(int arg1, int arg2, void *common)
{
	done = 1;
	return 0;
}

/* Callback thread */
int CallbackThread(SceSize args, void *argp)
{
	int cbid;

	cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
	sceKernelRegisterExitCallback(cbid);
	sceKernelSleepThreadCB();

	return 0;
}

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks(void)
{
	int thid = 0;

	thid = sceKernelCreateThread("update_thread", CallbackThread,
				     0x11, 0xFA0, 0, 0);
	if(thid >= 0)
	{
		sceKernelStartThread(thid, 0, 0);
	}

	return thid;
}

char g_internalDebugString[512] = {"PSP platform..."};
const char* __getDebugStatusString()
{
	return g_internalDebugString;
}

static unsigned int __attribute__((aligned(16))) list[262144];
static unsigned int __attribute__((aligned(16))) color_table[256];

void InitGU()
{
	void* fbp0 = 0;
	void* fbp1 = (void*)(BUF_WIDTH*SCR_HEIGHT*4); // RGBA_8888
	void* zbp = 0; // Do not use z-buffer

	sceGuInit();

	sceGuStart(GU_DIRECT,list);
	sceGuDrawBuffer(GU_PSM_8888,fbp0,BUF_WIDTH);
	sceGuDispBuffer(SCR_WIDTH,SCR_HEIGHT,fbp1,BUF_WIDTH);
	sceGuDepthBuffer(zbp,BUF_WIDTH);
	sceGuOffset(2048 - (SCR_WIDTH/2),2048 - (SCR_HEIGHT/2));
	sceGuViewport(2048,2048,SCR_WIDTH,SCR_HEIGHT);
	sceGuDepthRange(65535,0);
	sceGuScissor(0,0,SCR_WIDTH,SCR_HEIGHT);
	sceGuEnable(GU_SCISSOR_TEST);

	//sceGuDepthFunc(GU_GEQUAL);
	sceGuDisable(GU_DEPTH_TEST);
	sceGuDisable(GU_CULL_FACE);
	sceGuDisable(GU_LIGHTING);

	sceGuShadeModel(GU_SMOOTH);
	sceGuTexFilter(GU_LINEAR, GU_LINEAR);
	sceGuTexWrap(GU_CLAMP, GU_CLAMP);

	// Initialize the CLUT, which is used to support gray textures
	// The magic number 0x40000000 is very strange, my guess is that the high address is the mirror of non-cachable memory of the same one.
	unsigned int* clut = (unsigned int*)(((unsigned int)color_table)|0x40000000);
	for (int i = 0; i < 256; ++i) 
		clut[i] = (0x00ffffff | (i << 24));
	sceGuClutMode(GU_PSM_8888, 0, 0xff, 0);
	sceGuClutLoad(32, color_table);

	sceGuFinish();
	sceGuSync(0,0);

	sceDisplayWaitVblankStart();
	sceGuDisplay(GU_TRUE);
}

void ReadControllerStates(KGamePadState& outState)
{
	outState.Clear();
	SceCtrlData pad;
	sceCtrlReadBufferPositive(&pad, 1); 

	if (pad.Buttons & PSP_CTRL_SQUARE)
		outState.button_states |= KGamePadState::eSquare;
	if (pad.Buttons & PSP_CTRL_TRIANGLE)
		outState.button_states |= KGamePadState::eTriangle;
	if (pad.Buttons & PSP_CTRL_CIRCLE)
		outState.button_states |= KGamePadState::eCircle;
	if (pad.Buttons & PSP_CTRL_CROSS)
		outState.button_states |= KGamePadState::eCross;

	if (pad.Buttons & PSP_CTRL_UP)
		outState.button_states |= KGamePadState::eDPad_Up;
	if (pad.Buttons & PSP_CTRL_DOWN)
		outState.button_states |= KGamePadState::eDPad_Down;
	if (pad.Buttons & PSP_CTRL_LEFT)
		outState.button_states |= KGamePadState::eDPad_Left;
	if (pad.Buttons & PSP_CTRL_RIGHT)
		outState.button_states |= KGamePadState::eDPad_Right;

	if (pad.Buttons & PSP_CTRL_START)
		outState.button_states |= KGamePadState::eStart;
	if (pad.Buttons & PSP_CTRL_LTRIGGER)
		outState.button_states |= KGamePadState::eL1;
	if (pad.Buttons & PSP_CTRL_RTRIGGER)
		outState.button_states |= KGamePadState::eR1;

	outState.analogAxis0[0] = (float(pad.Lx) - 127.0f) / 127.0f;
	outState.analogAxis0[1] = (float(pad.Ly) - 127.0f) / 127.0f;
}

int main(void)
{
	pspDebugScreenInit();
	SetupCallbacks();
	pspDebugScreenSetXY(0, 0);

	// Configure controller settings
	sceCtrlSetSamplingCycle(0);
	sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);

	InitGU();

	if (InitApp()) {

		KTimer timer(true);
		while(!done){
			pspDebugScreenSetXY(0, 0);

			sceGuStart(GU_DIRECT,list);

			ReadControllerStates(KAppFrame::GetInstance()->mGamePadStatus);
			TimeElapse(timer.Stop());
		}

		DestoryApp();
	}

	sceGuTerm();
	sceKernelExitGame();
	return 0;
}

